#include "S5ActionBind.h"
#include <KSocket.h>
#include "S5ActionClient.h"
#include "S5Def.h"
#include "S5Server.h"

S5ActionBind::S5ActionBind(S5ActionClient *client)
    : KTimer(client->server()->queue()), m_listen(true), m_client(client)
{
    start(S5_TIME_OUT);
}

S5ActionBind::~S5ActionBind()
{
    assert(!isAlive());
}

void S5ActionBind::relay(const void *data, size_t length, const KIAddress *dest)
{
    server()->send(socket(), data, length);
}

void S5ActionBind::abortImp()
{
    // Close proxy side
    if (!m_listen) {
        server()->ensureAlive(socket(), false);
        socket()->shutdown();
    }
    socket()->close();
    // Close client side
    m_client->abortOnce();
    m_client = NULL;
}

void S5ActionBind::onAccepted(KSocket *client)
{
    stop();
    if (socket()->isAlive()) {
        KIpAddress address;
        client->getRemoteAddress(address);
        client->setKeepAlive(true, 5000, 1000);
        server()->ensureAlive(client);
        // Shift current action from server to client
        // This will later cause KNetItem::onFatalError()
        server()->setAction(socket());
        socket()->close();
        server()->setAction(client, this);
        m_listen = false;
        m_client->postSuccess(address);
        server()->receive(client);
    } else {
        client->shutdown();
        client->close();
    }
}

void S5ActionBind::onClosing(bool stopped)
{
    stop();
}

void S5ActionBind::onError(KDef::SockError type, DWORD error)
{
    if (m_listen) {
        onFatalError(type, error);
    } else {
        abortOnce();
    }
}

void S5ActionBind::onFatalError(KDef::SockError type, DWORD error)
{
    if (S5ActionClient *client = m_client) {
        client->postFailure(S5_REP_GENERAL_SERVER_FAILURE, false);
    }
}

void S5ActionBind::onReceived(KKey *&key, void *data, size_t length, bool more)
{
    S5Action::onReceived(key, data, length, more);
    if (S5ActionClient *client = m_client) {
        client->relay(data, length, NULL);
    }
}

void S5ActionBind::onSent(KKey *&key, void *data, size_t length)
{
    S5Action::onSent(key, data, length);
    if (S5ActionClient *client = m_client) {
        client->onPeerRelayed();
    }
}

void S5ActionBind::tick()
{
    // Request time out
    abortOnce();
}